#include "Material.h"
#include "Shader.h"
#include <memory>
#include <vector>
using namespace Umikaze::Core;

class ColorMaterial : public Material {
public:
  ColorMaterial(const std::shared_ptr<Shader> &shader) : Material(shader) {}

  void setColor(const glm::vec3 &color) { _color = color; }

  glm::vec3 _color;
};

class LightMaterial : public ColorMaterial {
public:
  LightMaterial(const std::shared_ptr<Shader> &shader)
      : ColorMaterial(shader) {}

  void setLight(const std::shared_ptr<Buffer> &lightBuffer) {
    _lightBuffer = lightBuffer;
  }

  std::shared_ptr<Buffer> _lightBuffer;
};

class UnLightMaterial : public ColorMaterial {
public:
  UnLightMaterial() : ColorMaterial(SHADER) {}

  // TODO:light不应该由context传过来，这样加阴影的时候很别扭，回头抽象个东西管light
  virtual void
  updateShader(const glm::mat4 &modelMatrix,
               const std::shared_ptr<Buffer> &cameraBuffer) override {
    if (_map) {
      _shader->setTexture("colorMap", _map);
    } else {
      _shader->setVec3("color", _color);
    }
    _shader->setMat4("modelMatrix", modelMatrix);
    _shader->setBuffer("cameraData", cameraBuffer);
  }
  std::shared_ptr<Texture> _map;

private:
  static std::shared_ptr<Shader> SHADER;
};

std::shared_ptr<Shader> UnLightMaterial::SHADER =
    std::make_shared<Shader>("UnLightShader.vert", "UnLightShader.frag");

class PhongMaterial : public LightMaterial {
public:
  PhongMaterial() : LightMaterial(SHADER) {}

  void setColorMap(const std::shared_ptr<Texture> &color) { _map = color; }

  void setShininess(float shin) { _shininess = shin; }

private:
  float _shininess = 64.f;
  std::shared_ptr<Texture> _map;

  virtual void
  updateShader(const glm::mat4 &modelMatrix,
               const std::shared_ptr<Buffer> &cameraBuffer) override {
    if (_map) {
      _shader->setTexture("colorMap", _map);
    } else {
      _shader->setVec3("color", _color);
    }
    _shader->setMat4("modelMatrix", modelMatrix);
    _shader->setFloat("shininess", _shininess);
    if (_lightBuffer) {
      _shader->setBuffer("Lights", _lightBuffer);
    }
    _shader->setBuffer("cameraData", cameraBuffer);
  }

  static std::shared_ptr<Shader> SHADER;
};

std::shared_ptr<Shader> PhongMaterial::SHADER =
    std::make_shared<Shader>("PhongMaterial.vert", "PhongMaterial.frag");

class PbrMaterial : public LightMaterial {
public:
  PbrMaterial() : LightMaterial(SHADER) {}

  void setRoughness(float roughness) {
    _roughness = std::max(roughness, 0.0f);
    _roughness = std::min(_roughness, 1.0f);
  }

  void setMetallic(float metallic) {
    _metallic = std::max(metallic, 0.0f);
    _metallic = std::min(_metallic, 1.0f);
  }

  void setShadowMap(const std::shared_ptr<Texture> &shadowMap,
                    const glm::mat4 &lightSpaceMatrix) {
    _shadowMap = shadowMap;
    _lightSpaceMatrix = lightSpaceMatrix;
  }

  void setShadowMapCascade(const std::shared_ptr<Texture> &cascadeShadowMap,
                           const std::shared_ptr<Buffer> &lightMatrices,
                           const std::vector<float> &cascadeSplit) {
    _cascadeShadowMap = cascadeShadowMap;
    _lightSpaceMatrices = lightMatrices;
    _matrixCount = cascadeSplit.size();
    _cascadeDistancePlanes = cascadeSplit;
  }

  //暂时只做一个主光源的阴影吧
  //多光源多阴影的话，会把事情弄得比较复杂
  //不太符合只做算法的本意
  std::shared_ptr<Texture> _shadowMap;
  glm::mat4 _lightSpaceMatrix;

  std::shared_ptr<Texture> _cascadeShadowMap;
  std::shared_ptr<Buffer> _lightSpaceMatrices;
  uint32_t _matrixCount;
  std::vector<float> _cascadeDistancePlanes;

  std::shared_ptr<Texture> _map;

private:
  virtual void
  updateShader(const glm::mat4 &modelMatrix,
               const std::shared_ptr<Buffer> &cameraBuffer) override {
    if (_map) {
      _shader->setTexture("colorMap", _map);
    } else {
      _shader->setVec3("color", _color);
    }
    _shader->setMat4("modelMatrix", modelMatrix);
    if (_lightBuffer) {
      _shader->setBuffer("Lights", _lightBuffer);
    }
    _shader->setBuffer("cameraData", cameraBuffer);
    _shader->setFloat("metallic", _metallic);
    _shader->setFloat("roughness", _roughness);

    if (_shadowMap) {
      _shader->setCondition("USE_SHADOWMAP", true);
      _shader->setCondition("USE_CASCADED_SHADOWMAP", false);
      _shader->setTexture("shadowMap", _shadowMap);
      _shader->setMat4("lightSpaceMatrix", _lightSpaceMatrix);
    } else if (_cascadeShadowMap) {
      _shader->setCondition("USE_SHADOWMAP", false);
      _shader->setCondition("USE_CASCADED_SHADOWMAP", true);
      _shader->setTexture("cascadeShadowMap", _cascadeShadowMap);
      _shader->setBuffer("lightSpaceMatricesData", _lightSpaceMatrices);
      _shader->setInt("level", _matrixCount);
      _shader->setFloatArray("cascadePlaneDistances", _cascadeDistancePlanes);
    } else {
      _shader->setCondition("USE_SHADOWMAP", false);
      _shader->setCondition("USE_CASCADED_SHADOWMAP", false);
    }
  }

private:
  float _roughness = 0.5;
  float _metallic = 0.5;
  static std::shared_ptr<Shader> SHADER;
};

// interal use

std::shared_ptr<Shader> PbrMaterial::SHADER =
    std::make_shared<Shader>("PbrMaterial.vert", "PbrMaterial.frag");